use crate::security::stock::{FinanceReport, FinanceReportItem, FinanceReportValue};
use crate::util::http;
use crate::util::http::HttpResponse;
use bytes::Bytes;
use csv::{Reader, StringRecord};
use encoding::all::GBK;
use encoding::{DecoderTrap, Encoding};

pub enum FinanceReportType {
    BalanceSheet,
    CashFlowStatement,
    IncomeStatement,
}
impl FinanceReportType {
    pub fn get_api_prefix(&self) -> &str {
        match self {
            FinanceReportType::BalanceSheet => "zcfzb",
            FinanceReportType::CashFlowStatement => "xjllb",
            FinanceReportType::IncomeStatement => "lrb",
        }
    }
}
///资产负债表

pub async fn load(
    stock_code: &str,
    report_type: &FinanceReportType,
) -> anyhow::Result<FinanceReport> {
    // http://quotes.money.163.com/service/zcfzb_601899.html
    println!("loading stock");
    let url = format!(
        "http://quotes.money.163.com/service/{}_{}.html",
        report_type.get_api_prefix(),
        stock_code
    );
    println!("url is {}", url);
    let res: HttpResponse = http::get(url.as_ref()).await?;
    println!("loading stock complete");
    parse_from_csv(&res.data)
}

fn parse_from_csv(csv_data: &Bytes) -> anyhow::Result<FinanceReport> {
    let data = GBK.decode(csv_data.as_ref(), DecoderTrap::Strict).unwrap();
    let rows = crate::util::csv::csv_to_vec(data.as_bytes(), true);
    let dates = rows.get(0).unwrap();
    let items = &rows[1..];

    let mut report: FinanceReport = FinanceReport::new(vec![]);
    for row_index in 0..items.len() {
        let item_datas = items.get(row_index).unwrap();
        let item_name = item_datas.get(0).unwrap();
        let mut item: FinanceReportItem = FinanceReportItem::new(item_name.clone(), vec![]);
        for col_index in 1..dates.len() {
            let date = dates.get(col_index);
            if let Some(d) = date.map(|e| e.trim()).filter(|e| !e.is_empty()) {
                let value = item_datas.get(col_index).map(|e| e.parse::<f64>());
                let value = match value {
                    Some(Ok(value)) => Some(value),
                    _ => Option::<f64>::None,
                };
                let item_value = FinanceReportValue::new(d.to_string(), value);
                item.push_value(item_value);
            }
        }
        report.push_value(item);
    }
    Ok(report)
}
